﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Web;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Drawing.Imaging;
using Microsoft.SharePoint;
using System.Linq;
using uk.ac.soton.ses.DAL;

namespace uk.ac.soton.ses.layouts
{
    public class ImageSliceHandler : IHttpHandler
    {
        /// <summary>
        /// You will need to configure this handler in the web.config file of your 
        /// web and register it with IIS before being able to use it. For more information
        /// see the following link: http://go.microsoft.com/?linkid=8101007
        /// </summary>
        #region IHttpHandler Members

        public bool IsReusable
        {
            // Return false in case your Managed Handler cannot be reused for another request.
            // Usually this would be false in case you have some state information preserved per request.
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
            //SPSecurity.RunWithElevatedPrivileges(delegate()
            //{
                WriteImageToHttpContext(context);
            //});
        }

        private void WriteImageToHttpContext(HttpContext context)
        {
            string sliceString = context.Request.QueryString["slice"];
            string xSizeString = context.Request.QueryString["xsize"];
            string ySizeString = context.Request.QueryString["ysize"];
            string zSizeString = context.Request.QueryString["zsize"];
            string experimentdatafileidString = context.Request.QueryString["experimentdatafileid"];
            string planeTypeString = (context.Request.QueryString["planetype"] ?? "null string passed").ToLower();
            string autocontrastString = (context.Request.QueryString["autocontrast"] ?? "null string passed").ToLower();

            int slice;
            int xsize;
            int ysize;
            int zsize;
            int experimentdatafileid;
            bool autocontrast;
            string fileName;
            if (!int.TryParse(sliceString, out slice)
                || !int.TryParse(xSizeString, out xsize)
                || !int.TryParse(ySizeString, out ysize)
                || !int.TryParse(zSizeString, out zsize)
                || !int.TryParse(experimentdatafileidString, out experimentdatafileid)
                || !new string[] { "xy", "yz", "xz" }.Contains(planeTypeString, StringComparer.OrdinalIgnoreCase)
                || !bool.TryParse(autocontrastString, out autocontrast)
                )
            {
                context.Response.StatusCode = 400;
                return;
            }
            using (MaterialsMetadataContext dbcontext = HttpContext.Current.GetMaterialsDBContext())
            {
                if (dbcontext.ExperimentDataFiles.Count(df => df.ID == experimentdatafileid) != 1)
                {
                    context.Response.StatusCode = 400;
                    return;
                }
                else
                {
                    ExperimentDataFile datafile = dbcontext.GetExperimentDataFileByID(experimentdatafileid);
                    if (datafile != null)
                    {
                        fileName = datafile.GetFullPath();

                        //Because File.Exists() does not check permissions
                        //(it just responds with doesn't exist rather than throwing an exception),
                        //we will just try to open the file and catch the exception
                        try
                        {
                            using (FileStream fs = File.OpenRead(fileName))
                            {
                            }
                        }
                        catch (UnauthorizedAccessException)
                        {
                            context.Response.StatusCode = 401;
                            return;
                        }
                        catch (Exception)
                        {
                            context.Response.StatusCode = 400;
                            return;
                        }
                    }
                    else
                    {
                        context.Response.StatusCode = 400;
                        return;
                    }
                }
            }

            context.Response.ContentType = "image/png";
            CtReader.Plane planeType;
            switch (planeTypeString)
            {
                case "xy":
                    planeType = CtReader.Plane.XY;
                    break;
                case "yz":
                    planeType = CtReader.Plane.YZ;
                    break;
                case "xz":
                    planeType = CtReader.Plane.XZ;
                    break;
                default:
                    context.Response.StatusCode = 400;
                    return;
            }

            Image img = null;
            try
            {
                img = drawPicture(fileName, slice, xsize, ysize, zsize, planeType, autocontrast);
                img.Save(context.Response.OutputStream, ImageFormat.Png);
            }
            catch (UnauthorizedAccessException e)
            {
                context.Response.ContentType = "text/plain";
                context.Response.Write(String.Format("Exception {0}", e.ToString()));
                //context.Response.StatusCode = 401;
                return;
            }
            catch (Exception)
            {
                context.Response.StatusCode = 400;
                return;
            }
            finally
            {
                if (img != null)
                {
                    img.Dispose();
                }
            }
        }


        private Image drawPicture(string fileName, int slice, int xsize, int ysize, int zsize, CtReader.Plane planeType, bool autoContrast)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                //Get a CtReader to process the file
                CtReader ctReader = new CtReader(fileName, new CtReader.Dimensions(xsize, ysize, zsize));

                //Extract the plane
                byte[] imagePlane = ctReader.ExtractPlane(slice, planeType, autoContrast);

                //Generate the bitmap source
                BitmapSource bmpSource = ctReader.GetPlaneAsBitmap(imagePlane, planeType, BitmapPalettes.Gray256);

                //Create a bitmap frame
                BitmapFrame bmpFrame = BitmapFrame.Create(bmpSource);

                //Encode the bitmap frame as a png
                PngBitmapEncoder pngenc = new PngBitmapEncoder();
                pngenc.Frames.Add(bmpFrame);

                //Save the png to the memory stream 
                pngenc.Save(ms);

                //Create an Image object from the memory stream
                Image img = Image.FromStream(ms);

                //Return the image
                return img;
            }
        }

        #endregion
    }
}
